package com.chaos.eurekaproducer.redis;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Pipeline;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author liaopeng
 * @title: JedisUtil
 * @projectName eureka-producer
 * @description: TODO
 * @date 2021/7/17下午7:04
 */
@Component
public class JedisUtil {

    Log log = LogFactory.getLog(JedisUtil.class);

    //redis分布式锁属性 start
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Long RELEASE_SUCCESS = 1L;
    //redis分布式锁属性 end

    private static JedisPool jedisPool;
    static {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(50);//连接池最大有多少个连接
        jedisPoolConfig.setMaxIdle(30);//连接池不繁忙时最多有多少个连接
        jedisPoolConfig.setMinIdle(20);//连接池不繁忙时最少有多少个连接
        jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);
    }

    /**
     * 直接获取jedis连接
     * @return
     */
    private Jedis getJedis() {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        String ping = jedis.ping();
        System.out.println("jedis尝试连接:"+ping);
        return jedis;
    }

    /**
     * 通过redis连接池获取jedis连接
     * @return
     */
    private Jedis getJedisByPool() {
        Jedis jedis = jedisPool.getResource();
        String ping = jedis.ping();
        System.out.println("jedis尝试连接:"+ping);
        return jedis;
    }
    /**
     * 尝试获取分布式锁
     *
     * @param lockKey    锁
     * @param requestId  请求标识
     * @param expireTime 超期时间 毫秒
     * @return 是否获取成功, 如果获取失败则不能执行锁内逻辑，进行操作失败提示
     */
    public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
        Jedis jedis = null;
        try {
            jedis = getJedisByPool();
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            jedis.close();
        }
        return false;
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey   锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String lockKey, String requestId) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            jedis.close();
        }
        return false;
    }

    /** ------------------------------------------可重入的分布式锁,使用ThreadLocal实现------------------------------------------------------*/
    private ThreadLocal<Map<String,Integer>> lockers = new ThreadLocal<>();

    /**
     * 可重入分布式锁 加锁
     * @param lockKey
     * @param requestId
     * @param expireTime
     * @return
     */
    public boolean tryReentrantLock(String lockKey,String requestId,int expireTime){
        Map<String, Integer> refs = currentLockers();
        Integer count = refs.get(lockKey);
        if (count != null){
            refs.put(lockKey,count+1);
            return true;
        }
        boolean getLock = tryGetDistributedLock(lockKey, requestId, expireTime);
        if (getLock){
            refs.put(lockKey,1);
            return true;
        }
        return false;
    }

    /**
     * 可重入分布式锁 释放锁
     * @param lockKey
     * @param requestId
     * @return
     */
    public boolean releaseReentrantLock(String lockKey,String requestId){
        Map<String, Integer> refs = currentLockers();
        Integer count = refs.get(lockKey);
        if (count == null){
            return false;
        }
        count -=1;
        if (count >0){
            refs.put(lockKey,count);
        }else {
            refs.remove(lockKey);
            releaseDistributedLock(lockKey,requestId);
        }
        return true;
    }


    public Map<String,Integer> currentLockers(){
        Map<String, Integer> refs = lockers.get();
        if (refs != null){
            return refs;
        }
        lockers.set(new HashMap<>());
        return lockers.get();
    }
}

